﻿namespace MetaProperties.Core
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;

    using MetaProperties.Framework;

    /// <summary>
    /// Class to which the role of a MetaPropertyHost can be largely delegated.
    /// </summary>
    public class MetaPropertyHostHelper : IMetaPropertyHost
    {
        /// <summary>
        /// The dictionary of MetaProperties by name.
        /// </summary>
        private readonly Dictionary<string, IMetaProperty> metaProperties = new Dictionary<string, IMetaProperty>();

        /// <summary>
        /// The host of the MetaProperties.
        /// </summary>
        private readonly IMetaPropertyHost host;

        /// <summary>
        /// Initializes a new instance of the <see cref="MetaPropertyHostHelper"/> class.
        /// </summary>
        /// <param name="host">The host of the MetaProperties.</param>
        public MetaPropertyHostHelper(IMetaPropertyHost host)
        {
            this.host = host;
        }

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Gets all the public MetaProperties in the container.
        /// </summary>
        /// <value>The stream of public MetaProperties.</value>
        public IEnumerable<IMetaProperty> MetaProperties
        {
            get
            {
                foreach (IMetaProperty metaProperty in this.metaProperties.Values)
                {
                    yield return metaProperty;
                }
            }
        }

        /// <summary>
        /// Initializes the public MetaProperties.
        /// </summary>
        public void InitializePublicMetaProperties()
        {
            foreach (var metaProperty in MetaPropertyUtils.InitializeMetaProperties(this.host))
            {
                this.AttachToMetaProperty(metaProperty);
            }
        }

        /// <summary>
        /// Called when [property changed].
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        public void OnPropertyChanged(string propertyName)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this.host, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Gets the requested MetaProperty.
        /// </summary>
        /// <param name="propertyName">The name of the property.</param>
        /// <returns>
        /// The requested MetaProperty if it exists; null otherwise.
        /// </returns>
        public IMetaProperty GetMetaProperty(string propertyName)
        {
            IMetaProperty metaProperty;
            this.metaProperties.TryGetValue(propertyName, out metaProperty);
            return metaProperty;
        }

        /// <summary>
        /// Attaches to MetaProperty.
        /// </summary>
        /// <param name="metaProperty">The MetaProperty.</param>
        private void AttachToMetaProperty(IMetaProperty metaProperty)
        {
            if (this.metaProperties.ContainsKey(metaProperty.PropertyName))
            {
                throw new InvalidOperationException("MetaProperty '" + metaProperty.PropertyName + "' has already been added to the host.");
            }

            this.metaProperties.Add(metaProperty.PropertyName, metaProperty);
            metaProperty.PropertyChanged += this.MetaProperty_PropertyChanged;
        }

        /// <summary>
        /// Handles the PropertyChanged event of the MetaProperty control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void MetaProperty_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                IMetaProperty metaProperty = (IMetaProperty)sender;
                handler(this.host, new PropertyChangedEventArgs(metaProperty.PropertyName));
            }
        }
    }
}
